Temporal Server
Platform:
| Channel | Revision | Published | Runs on |
|---|---|---|---|
| 1.23/stable | 96 | Yesterday | |
| 1.23/stable | 60 | 30 Apr 2026 | |
| 1.23/edge | 96 | Yesterday | |
| 1.23/edge | 60 | 09 Dec 2025 | |
| 1.31/stable | 95 | Yesterday | |
| 1.31/edge | 95 | Yesterday | |
| 1.31/edge | 75 | 04 Aug 2026 | |
| 1.30/edge | 90 | 16 Oct 2026 | |
| 1.29/edge | 88 | 16 Oct 2026 | |
| 1.28/edge | 87 | 16 Oct 2026 | |
| 1.27/edge | 86 | 16 Oct 2026 | |
| 1.26/edge | 83 | 15 Oct 2026 | |
| 1.25/edge | 91 | 16 Oct 2026 | |
| 1.24/edge | 84 | 16 Oct 2026 |
juju deploy temporal-k8s --channel 1.23/stable
Configure ingress with the ingress interface
Charmed Temporal’s frontend can be exposed to clients outside the cluster through the
standard ingress interface. The Temporal frontend is a gRPC (HTTP/2) server, so it
is exposed through the Ingress Configurator
fronted by HAProxy, which handles gRPC load balancing over
TLS.
This topology requires TLS end-to-end. Ingress Configurator with HAProxy does not
support plaintext HTTP/2 (h2c) to the backend, so for this topology the Temporal frontend
must terminate TLS itself: integrate frontend-certificates alongside ingress, and the
proxy re-encrypts to it.
The charm does not enforce this, because other providers do not need it. The advertised
scheme follows what the frontend actually serves - https with frontend-certificates,
h2c without - and the charm cannot tell which provider is on the other end of the
relation. Relating ingress without frontend-certificates is therefore allowed and
logs a warning; with HAProxy the result is simply that the proxy cannot reach the
backend. See Other ingress providers for the h2c path.
Only the frontend service can be exposed, and only one ingress solution can be used at
a time.
Prerequisites
- A TLS certificate provider charm (for example self-signed-certificates) - one instance on the Kubernetes model (for the frontend cert) and one where HAProxy runs (for the client-facing cert).
- HAProxy (
2.8/edgeor later). HAProxy is a machine charm, so it runs on a separate machine model and is related to the Kubernetes applications through a cross-model integration. - Network reachability from HAProxy to the Temporal frontend’s Kubernetes endpoint (see Backend reachability).
- The Temporal CLI snap for connecting as a client.
Throughout this guide the routing hostname is temporal-k8s.test; substitute your own.
1. Make the frontend serve gRPC over TLS
On the Kubernetes model, give the Temporal frontend a certificate so it terminates TLS on its gRPC port:
juju deploy self-signed-certificates
juju integrate temporal-k8s:frontend-certificates self-signed-certificates:certificates
Verify the frontend is now serving TLS (ALPN negotiates HTTP/2):
juju ssh --container temporal temporal-k8s/0 grep -nE 'certFile|keyFile' /etc/temporal/config/charm.yaml
2. Deploy and configure the Ingress Configurator (Kubernetes model)
juju deploy ingress-configurator --channel latest/edge --trust
juju integrate temporal-k8s:ingress ingress-configurator:ingress
juju config ingress-configurator hostname=temporal-k8s.test backend-protocol=https
backend-protocol=https tells the proxy to connect to the frontend over TLS. Setting
external-grpc-port is optional (gRPC is served on 443 by default).
3. Deploy and configure HAProxy (machine model)
On the machine model:
juju deploy haproxy --channel 2.8/edge
juju deploy self-signed-certificates
juju integrate haproxy:certificates self-signed-certificates
juju config haproxy external-hostname=temporal-k8s.test
external-hostname is required - without it HAProxy never requests its own client-facing
certificate and its :443 listener fails to validate (“unable to stat SSL certificate”).
4. Wire the cross-model integrations
HAProxy (machine) and the Kubernetes applications live on different models, so use offers.
juju offer is run from the offering model (no controller prefix); juju consume uses the
fully-qualified <controller>:admin/<model>.<application> offer URL.
Route the ingress traffic to HAProxy:
# machine model
juju offer haproxy:haproxy-route
# kubernetes model
juju consume <machine-controller>:admin/<machine-model>.haproxy haproxy-cmr
juju integrate ingress-configurator:haproxy-route haproxy-cmr
Let HAProxy trust the frontend’s CA so the re-encrypted backend hop verifies (the CA is the certificate provider on the Kubernetes model):
# kubernetes model
juju offer self-signed-certificates:send-ca-cert
# machine model
juju consume <k8s-controller>:admin/<k8s-model>.self-signed-certificates frontend-ca
juju integrate haproxy:receive-ca-certs frontend-ca
Backend reachability
HAProxy must be able to reach the Temporal frontend’s Kubernetes address. If HAProxy cannot
route to the frontend’s ClusterIP, expose the frontend on a routable address (for example
a NodePort or LoadBalancer service) and point the configurator at it:
juju config ingress-configurator backend-addresses=<node-ip> backend-ports=<node-port>
5. Connect a client
Map the routing hostname to HAProxy’s public address, then connect over TLS. Fetch the CA that signed HAProxy’s client-facing certificate (the certificate provider on the machine model) and pass it to the client:
echo "<haproxy-ip> temporal-k8s.test" | sudo tee -a /etc/hosts
temporal operator namespace list \
--address temporal-k8s.test:443 \
--tls \
--tls-ca-path <haproxy-ca>
The full path is TLS end-to-end: client → HAProxy (TLS) → Temporal frontend (TLS), with HAProxy verifying the frontend against the CA received in step 4.
Other ingress providers
The ingress interface is provider-agnostic, so temporal-k8s:ingress can be related to
any charm that implements it.
-
traefik-k8s serves gRPC over cleartext HTTP/2, so it works without
frontend-certificates: the charm advertises theh2cscheme and the frontend stays plaintext, leaving in-model clients untouched. Traefik must use subdomain routing:juju config traefik-k8s routing_mode=subdomainWith the default path routing, Traefik exposes the application under a
/<model>-<application>path prefix. A gRPC request’s path is its service and method name, so the prefix reaches the frontend and calls fail withgrpc-status: 12 unknown service; gRPC clients have no way to add the prefix. With subdomain routing each application gets its own hostname and the path is left intact. Traefik is approaching end-of-life and is not part of our test suite, so it is left to the user to configure. -
The Gateway API Integrator implements the same interface, but gRPC over TLS additionally needs the gateway to negotiate the
h2ALPN protocol. On Canonical Kubernetes the bundled Cilium gateway does not currently expose ALPN configuration, so this path is not supported there.